package com.yunsom.testdemo.java8.stream;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Xbao
 * @version 1.0
 * @description
 * @date 2021/3/15 18:42
 */
public class StreamTest {

  public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    System.out.println("这个函数将应用到每一个item");
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
  }

  public static void main(String[] args) {

    BigDecimal systemSaveCount = BigDecimal.valueOf(1000d);
    BigDecimal dealSystemSaveCount = BigDecimal.valueOf(20d);
    System.out.println(systemSaveCount.subtract(dealSystemSaveCount));


    List<String> users = Arrays.asList("张三", "李四", "法外狂徒");
    List<Student> students = new ArrayList<>(16);
    students.add(new Student("1", "张三", 18, "male", 88d, LocalDateTime.now()));
    students.add(new Student("3", "张三", 18, "male", 100d, LocalDateTime.now()));
    students.add(new Student("5", "董七", 14, "female", 95d, LocalDateTime.now()));
    students.add(new Student("7", "老九", 20, "unknown", 66d, LocalDateTime.now()));
    students.add(new Student("9", "小红", 22, "female", 95d, LocalDateTime.now()));
    students.add(new Student("9", "小红1", 24, "female", 95d, LocalDateTime.now()));

    //去重 distinct
    List<String> distinctNames = users.stream().distinct().collect(Collectors.toList());
    distinctNames.forEach(System.out::print);

    // 根据对象中某字段去重
    students = students.stream().distinct().collect(
            Collectors.collectingAndThen(
                    Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getName))), ArrayList::new
            )
    );

    
    List<Student> list2 = students.stream().filter(distinctByKey(Student::getId)).collect(Collectors.toList());
    System.out.println("--");


    //归集并去重
//    Map<String,String> attrNames = students.stream().collect(
//        Collectors.toMap(
//            Student::getId,
//            student -> student.getName(),
//            (entity1, entity2) -> entity1
//        )
//    );
//    System.out.println(attrNames);

    //遍历 forEach（）
    students.stream().forEach(System.out::println);
    System.out.println("----------");

    //过滤 filter
    List<Student> fullMarkStudents = students.stream().filter(student -> student.getScore()==100).collect(
        Collectors.toList());
    fullMarkStudents.stream().forEach(System.out::println);
    System.out.println("----------");

    //查找 findFirst findAny
    Optional<Student> studentOptional = students.stream().findFirst();
    if (studentOptional.isPresent()){
      Student student = studentOptional.get();
      System.out.println(student);
    }
    Optional<Student> studentOptional1 = students.parallelStream().filter(student -> student.getAge()>=20).findAny();
    studentOptional1.ifPresent(System.out::println);
    System.out.println("----------");

    //匹配 match
    boolean anyMatch = students.stream().anyMatch(student -> student.getScore()==100);
    boolean allMatch = students.stream().allMatch(student -> student.getScore()>60);
    boolean nonMatch = students.stream().noneMatch(student -> student.getScore()==100);
    System.out.println(anyMatch);
    System.out.println(allMatch);
    System.out.println(nonMatch);
    System.out.println("----------");

    //映射 map flatmap
    //获取所有姓名
    List<String> names = students.stream().map(Student::getName).collect(Collectors.toList());
    System.out.println(names);
    List<String> firstNames = names.stream().flatMap(s -> Arrays.stream(s.split(""))).collect(Collectors.toList());
    System.out.println(firstNames);
    //每个学生成绩加10分
    List<Double> scores = students.stream().map(student -> student.getScore()+10).collect(Collectors.toList());
    System.out.println(scores);
    System.out.println("----------");

    //截取流 limit方法用于获取指定数量的流。
    //取出成绩大于70的5个人
    List<Student> scoreLargerThanSeventy = students.stream().filter(student -> student.getScore()>70).limit(5).collect(
        Collectors.toList());
    scoreLargerThanSeventy.forEach(System.out::println);
    //跳过第一个取两个
    List<Student> students2 = students.stream().skip(1).limit(2).collect(Collectors.toList());
    //获取5个随机int，从小到大排列
    Random random = new Random();
    random.ints().limit(5).sorted().forEach(System.out::println);
    System.out.println("----------");

    //排序 sorted
    // 按自然排序倒序
    List<Student> students3 = students.stream().sorted(Comparator.comparing(Student::getBirth).reversed()).collect(Collectors.toList());
    students3.forEach(System.out::println);
    // 按自然排序升序
    students3 = students.stream().sorted(Comparator.comparing(Student::getBirth).reversed()).collect(Collectors.toList());
    students3.forEach(System.out::println);
    System.out.println("----sort------");
    List<Student> students4 = students.stream().sorted(Comparator.comparing(Student::getScore).thenComparingInt(Student::getAge)).collect(
        Collectors.toList());
    students4.forEach(System.out::println);
    System.out.println("----------");

    //统计 summaryStatistics
    DoubleSummaryStatistics doubleSummaryStatistics = students3.stream().mapToDouble(Student::getScore).summaryStatistics();
    System.out.println("成绩平均值"+doubleSummaryStatistics.getAverage());
    System.out.println("成绩总数量"+doubleSummaryStatistics.getCount());
    System.out.println("成绩最大值"+doubleSummaryStatistics.getMax());
    System.out.println("成绩最小值"+doubleSummaryStatistics.getMin());
    System.out.println("成绩之和"+doubleSummaryStatistics.getSum());
    System.out.println("----------");

    //归约 reduce 归约，把一个流归约(缩减)成一个值，能实现对集合求和、求乘积和求最值等操作。
    List<Integer> integerList = Arrays.asList(6, 7, 1, 9, 11, 7, 13, 20);
    //求和
    Optional<Integer> sum1 = integerList.stream().reduce(Integer::sum);
    //求和 基于9的基础上
    Integer sum2 = integerList.stream().reduce(9,Integer::sum);
    // decimal字段使用流的方式求和
    List<BigDecimal> decimals = new ArrayList<>();
    BigDecimal sum = decimals.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
    //max
    Optional<Integer> max1 = integerList.stream().reduce((x,y) -> x>y ? x : y);
    //max based on a exsiting number : 50
    Integer max2 = integerList.stream().reduce(50,(x,y) -> x>y ? x : y);
    Optional<Integer> min = integerList.stream().reduce(Integer::min);
    //求乘积
    Optional<Integer> product = integerList.stream().reduce((x,y) -> x*y);
    System.out.println("原始集合：" + integerList);
    System.out.println("集合求和：" + sum1.get() + "," + sum2);
    System.out.println("集合求最大值：" + max1.get() + "," + max2);
    System.out.println("集合求最小值：" + min.get());
    System.out.println("集合求积：" + product.get());
    System.out.println("----------");

    //归集 toList，toSet，toMap
    // 获取年龄大于等于15的年龄set集合
    Set<Integer> ages = students.stream().map(Student::getAge).filter(n -> n >= 15).collect(Collectors.toSet());
    System.out.println(ages);
    // 创建学生ID和学生实体的map
//    Map<String,Student> studentMap = students.stream().collect(Collectors.toMap(Student::getId, student -> student));
//    studentMap.forEach((key , value) -> System.out.println(key + ":"+value));
//    System.out.println("----------");

    //分组 partitioningBy，groupingBy
    //partitioningBy（分区）：stream中的元素按条件被分为两个Map。
    //groupingBy（分组）：stream中的元素按条件被分为多个Map。
    Map<Boolean,List<Student>>  booleanListMap = students.stream().
        collect(Collectors.partitioningBy(student -> student.getScore()>=60));
    booleanListMap.forEach((key, value) -> System.out.println(key+":"+value));

    Map<String,List<Student>> stringListMap = students.stream()
        .collect(Collectors.groupingBy(Student::getSex));
    stringListMap.forEach((key, value) ->{
      System.out.println("性别:"+key);
      value.forEach(System.out::println);
    });
    System.out.println("---------");

    //合并 joining  将stream中的元素用指定的连接符（没有的话，则直接连接）连接成一个字符串。
    String joinName = distinctNames.stream().collect(Collectors.joining("-"));
    System.out.println(joinName);

    System.out.println(System.currentTimeMillis());

    //操作集合中的元素
    ArrayList<Student> studentss  = new ArrayList<>();
    List<Teacher> teachers = studentss.stream().map(p -> {
      Teacher teacher = new Teacher();
      teacher.setName(p.getName());
      return teacher;
    }).collect(Collectors.toList());

    // stream把集合中类的两个字段转成map
    Map<String, String> map = students.stream().collect(Collectors.toMap(Student::getId, Student::getName,(k1,k2)->k2));

    // stream转map，重复key的处理
    Map<String, String> map1 = students.stream().collect(Collectors.toMap(Student :: getId, Student :: getName,
        (key1 , key2)-> key2 ));


    // 多条件排序
    List<Tt> list = new ArrayList<>();

    list.add(new Tt(1,2, "1"));
    list.add(new Tt(2,2, "2"));
    list.add(new Tt(5,3, ""));
    list.add(new Tt(5,1, "3"));
    list.add(new Tt(5,2, "1"));
    list.add(new Tt(4,2, "4"));
    list.add(new Tt(7,2, "3"));
    list.add(new Tt(3,2, "2"));

    // 当须要用到stram多条件排序的时候，须要最后排序的字段须要放在前面排，改后代码以下：
//    List<Tt> sortedList = list.stream()
//            .sorted(Comparator.comparing(Tt::getCode).reversed())
//            .sorted(Comparator.comparing(Tt::getLine))
//            .sorted(Comparator.comparing(Tt::getId))
//            .collect(Collectors.toList());

    // 测试多条件排序
//    List<Tt> sortedList = list.stream() .sorted(Comparator.comparing(Tt::getCode).reversed()).collect(Collectors.toList());
//    sortedList = sortedList.stream().sorted(Comparator.comparing(Tt::getLine)).collect(Collectors.toList());
//    sortedList = sortedList.stream().sorted(Comparator.comparing(Tt::getId)).collect(Collectors.toList());

    List<Tt> sortedList = list.stream() .sorted(Comparator.comparing(Tt::getCode)).collect(Collectors.toList());

    System.err.println(sortedList);

    System.out.println("--end--");

    Map<Long, List<Student>> studentMap = new HashMap<>();
    studentMap.put(1L, list2);
    List<Student> list3 =studentMap.get(2L);
    for (Student student : list3) {
      System.out.println("1");
    }

    List<Student> xlist1 = new ArrayList<>();
    List<Student> xlist2 = new ArrayList<>();
    // 交集
    List<Student> intersection = xlist1.stream().filter(xlist2::contains).collect(Collectors.toList());
    System.out.println("---交集---"+intersection);

    // 差集 (list1 - list2)
    List<Student> reduce1 = xlist1.stream().filter(item -> !xlist2.contains(item)).collect(Collectors.toList());
    System.out.println("---差集 (list1 - list2)---"+reduce1);

    // 差集 (list2 - list1)
    List<Student> reduce2 = xlist2.stream().filter(item -> !xlist1.contains(item)).collect(Collectors.toList());
    System.out.println("---差集 (list2 - list1)---"+reduce2);

    // 并集
    xlist1.addAll(xlist2);
    System.out.println("---并集 list---"+xlist1);

    // 去重并集，方法有很多这里只列举一种
    HashSet<Student> hashSet = new HashSet<>(xlist1);
    System.out.println("---去重并集 list---"+hashSet);

  }

}
